/*
  gutil.c
  Description: util for GIGI, including system porting, socket, string, gigi object, 
  Created: 2009.10.26
  Version: 1.0
*/

#ifdef HAVE_CONFIG_H
  #include "config.h"
#endif

#include "gutil.h"
#include "gconn.h"
#include <assert.h>

#define THIS_FILE_ERROR_ID  20000

static int fd_send_argv (GIGI_STREAM s, int type, const void * buff, int count);
static int fd_recv_argv (GIGI_STREAM s, int type, void * buff, int * count);
static int fd_send_stream (GIGI_STREAM s, int type, const void * buff, int count);
static int fd_recv_stream (GIGI_STREAM s, int type, void * buff, int * count);

struct gigi_type_def_t  g_typeInline[]=
{ 
  {GIGI_TYPE_ARGV,   fd_send_argv,   fd_recv_argv},
  {GIGI_TYPE_STREAM, fd_send_stream, fd_recv_stream},
  {0,0,0}
};

int             g_typeLast= 1000;
ring_root       g_typesRoot = {&g_typesRoot, &g_typesRoot };
struct gigi_type_list_t     g_typesDef  = {NULL, NULL, g_typeInline};
GIGI_TYPE_DEF   g_typeCache[128]={0};
int             g_typeSize[7] = {0, 1, 2, 4, 8, 8};
#define         GIGI_TYPE_CACHE_ID(id) ( ((unsigned)(id)) & 0x7F )
#define         GIGI_TYPE_CACHE(id)    ( g_typeCache + GIGI_TYPE_CACHE_ID(id) )


/* function gigi_type_register
 *
 */
int gigi_type_register ( GIGI_TYPE_LIST ty, int reg)
{
  if (reg)
  {
    GIGI_TYPE_DEF t;
    RING_PUSH_FRONT (&g_typesRoot, ty);

    for (t = ty->_types; t->_send; t++)
    {
      if (t->_type == 0)
      {
        t->_type = g_typeLast++;
      }
    }
  }
  else
  {
    memset (g_typeCache, 0, sizeof(g_typeCache));
    RING_DEL (ty);
    RING_INIT (ty);
  }

  return GIGI_OK;
}

/* function gigi_type 
 *
 */
GIGI_TYPE_DEF gigi_type (int type)
{
  GIGI_TYPE_LIST i, iEND;
  GIGI_TYPE_DEF * pCache = GIGI_TYPE_CACHE ( type );
  
  if (*pCache && (*pCache)->_type == type )
  {
    return *pCache;
  }

  if ( g_typesDef._next == NULL )
  {
    RING_PUSH_FRONT (&g_typesRoot, &g_typesDef);
  }

  i    = RING_BEGIN(&g_typesRoot, GIGI_TYPE_LIST);
  iEND = RING_END(&g_typesRoot, GIGI_TYPE_LIST);
  
  for (; i!=iEND; RING_NEXT (i) )
  {
    GIGI_TYPE_DEF di = i->_types;

    for ( ; di->_send ; di++)
    {
      if (di->_type == type )
      {
        *pCache = di;
        return di;
      }
    }
  }

  return NULL;
}

#ifdef GIGI_BIG_ENDIAN

char * byte_change_buf (char * pFrom, char * pEnd,int type)
{
  char * p = pFrom;

  if ( type == GIGI_TYPE_INT16 )
  {
    for( ; p < pEnd; p += 2 )
    {
      BYTE_CHANGE_INT16 ( *((unsigned short*)p) );
    }
  }
  else if (type == GIGI_TYPE_INT32)
  {
    for( ; p < pEnd; p += 4 )
    {
      BYTE_CHANGE_INT32 ( *((unsigned int *)p) );
    }
  }
  else if ( type == GIGI_TYPE_INT64 )
  {
    for( ; p < pEnd; p += 8 )
    {
      BYTE_CHANGE_INT64 ( *((GIGI_UINT64*)p) );
    }
  }

  return pFrom;
};
#else
#define byte_change_buf(a,b,c) do {} while(0)
#endif

#define FD_SEND_W(buf,sz) \
  n = gigi_fwrite(s,buf,sz); ERR_RETURN(n); nSended+=n;

/* function fd_send_object
 *
 */
int fd_send_object (GIGI_STREAM s, int type, const void * buff, int  count)
{
  int isArray  = type & GIGI_TYPE_ARRAY;
  int isPtrAddr= type & GIGI_TYPE_PTR;
  int baseType = type & GIGI_TYPE_MASK;
  int n = 0, i, nSended = 0;

  if (isPtrAddr)
  {
    buff = *( (void **)buff );
    type = type & ~GIGI_TYPE_PTR;
  };

// Step 1: for INT32, INT64, BUFFER, PTR_BUF
//
  switch (type)
  {
    case GIGI_TYPE_INT32:
    {
#ifdef GIGI_BIG_ENDIAN
      unsigned int v = HOST_BYTE_INT32 ( * ((unsigned int *)buff) );
      buff = &v;
#endif
      return gigi_fwrite (s, buff, 4);
    }; 
    break;

    case GIGI_TYPE_INT64:
    {
#ifdef GIGI_BIG_ENDIAN
      GIGI_UINT64 v = HOST_BYTE_INT64 ( * ((GIGI_UINT64*)buff) );
      buff = &v;
#endif
      return gigi_fwrite (s, buff, 8);
    }; break;

    case GIGI_TYPE_STRING:
      if ( count < 0 )
      {
        count = ( buff && *((char *)buff) ) 
                ? (int)strlen ( (char *)buff ) + 1 : 0;
      };

    case GIGI_TYPE_BUFFER :
      {
        unsigned v = HOST_BYTE_INT32 ( count );

        FD_SEND_W(&v,4);

        if ( count > 0 )
        {
          FD_SEND_W(buff, count);
        }

        return nSended;
      };
  }

// Step 3 : for standard type (including array)
//
  assert( isPtrAddr == 0 || baseType < GIGI_TYPE_OBJECT_BEGIN );

  if (baseType < GIGI_TYPE_OBJECT_BEGIN )
  {
    int bufSize = isArray ? count * g_typeSize [baseType] 
                          : g_typeSize [baseType];

    if (isArray)
    {
      unsigned int nC =HOST_BYTE_INT32(count);
      FD_SEND_W(&nC,4);
    }

#ifdef GIGI_BIG_ENDIAN
    if ( baseType == GIGI_TYPE_INT32 ||
       baseType == GIGI_TYPE_INT16 ||
       baseType == GIGI_TYPE_INT64 )
    {
      char sBuf[1024], *pD = (char *)buff;
      
      while ( bufSize > 0 )
      {
        int nST = bufSize < sizeof (sBuf) ? bufSize : sizeof (sBuf);
        bufSize -= nST;

        memcpy (sBuf, pD, nST);
        byte_change_buf (sBuf, sBuf + nST, baseType);
        FD_SEND_W (sBuf, nST);

        bufSize -= nST;
        pD += nST;
      }

      return nSended;
    }
#endif

    if (bufSize > 0)
    {
      FD_SEND_W(buff, bufSize);
    }

    return nSended;
  }

// Step 6 : for Array Type
//
  if (isArray)
  {
    void ** pp = (void **)buff;
    unsigned int nC =HOST_BYTE_INT32(count);
    FD_SEND_W(&nC,4);

    for (i=0; i < count; i++, pp++)
    {
      n = fd_send_object (s, baseType, *pp, -1);
      ERR_RETURN (n);
      nSended += n;
    }

    return nSended;
  }

// Step 5 : for extended type
//
  if ( type >= GIGI_TYPE_OBJECT_BEGIN )
  {
    GIGI_TYPE_DEF ty = gigi_type (type);
    
    return ty ? ty->_send (s, type, buff, count ) : GIGI_E_INTERNAL_ERROR;
  }

  return GIGI_E_INTERNAL_ERROR;
}

#define FD_RECV_R(buf,sz) \
  n = gigi_fread (s, buf, sz); ERR_RETURN (n); nRecved += n;


/* function fd_recv_object
 *
 */
int fd_recv_object (GIGI_STREAM s, int type, void * buff, int * nParamCount)
{
  int isArray, isPtrAddr, baseType, n = 0, i, nRecved = 0, nReadedCount;
  int nCountTmp=0, *count = nParamCount ? nParamCount : &nCountTmp;
  int nCountBuf = *count;

/* int bo = 0;
 * gigi_fcntl (s, GIGI_FIO_GET_BO, &bo);
 */


/*
 * Step 1: for INT32, for INT64
 */
  switch ( type )
  {
    case GIGI_TYPE_INT32:
    {
      FD_RECV_R(buff,4);
      /* BO_CHGV_AUTO(32,bo,buff); */
      BYTE_CHANGE_INT32 ( *((unsigned*)buff) );
      return nRecved;
    }

    case GIGI_TYPE_INT64:
    {
      FD_RECV_R(buff,8);
      BYTE_CHANGE_INT64 ( *((GIGI_UINT64*)buff) );
      return nRecved;
    }

    case GIGI_TYPE_STRING :
    {
      n = fd_recv_object(s, GIGI_TYPE_BUFFER, buff, count);
      ERR_RETURN (n);
      ((char *)buff) [ *count < nCountBuf ? *count : nCountBuf - 1] = 0;
      return n;
    }

    case GIGI_TYPE_PTR_STRING :
    {
      n = fd_recv_object(s, GIGI_TYPE_PTR_BUF, buff, count);
      ERR_RETURN (n);
      ((char *)buff) [ *count ] = 0;
      return n;
    }
  }

 /*
  * Step 2: for standard type
  */

  isArray    = type & GIGI_TYPE_ARRAY;
  isPtrAddr  = type & GIGI_TYPE_PTR;
  baseType   = type & GIGI_TYPE_MASK;

  assert( isPtrAddr == 0 || baseType < GIGI_TYPE_OBJECT_BEGIN );

  if ( baseType < GIGI_TYPE_OBJECT_BEGIN )
  {
    if (!isArray)
    {
      FD_RECV_R(buff, g_typeSize [baseType]);
      *count = 1;
    }
    else
    {
      int nConsumeCount;

      FD_RECV_R(&nReadedCount, 4);

      BYTE_CHANGE_INT32 (nReadedCount);

      if ( isPtrAddr )
      {
        int buff_size = ( g_typeSize [ baseType ] * (nReadedCount + 1) + 7 ) & ~7;
      
        *((void **)buff) = malloc ( buff_size );
        buff = *( (void **)buff );

        if (buff == NULL)
        {
          LOG_OUT_OF_MEMORY();
          return GIGI_E_OUT_OF_MEMORY;
        }
      
        nCountBuf     = nReadedCount + 1;
        nConsumeCount = 0;
      }
      else
      {
        nConsumeCount = nReadedCount > nCountBuf ? nReadedCount - nCountBuf : 0;
      }

      *count = nReadedCount - nConsumeCount;
      
      if ( *count > 0 )
      {
        FD_RECV_R(buff, *count * g_typeSize [baseType]);
      }

      if (nConsumeCount > 0 )
      {
        gigi_fread (s, NULL, nConsumeCount * g_typeSize [ baseType ]);
      };
    };

  #ifdef GIGI_BIG_ENDIAN
    switch ( baseType )
    {
    case GIGI_TYPE_INT16 :
    case GIGI_TYPE_INT32 :
    case GIGI_TYPE_INT64 :
      {
        byte_change_buf ( 
           (char *)buff, 
           (char *)buff + *count * g_typeSize [baseType], 
           baseType );
      }break;
    };
  #endif

    return nRecved;
  }

/* Step 3 : for Array Type
 *
 */
  if (isArray)
  {
    void ** pp = (void **)buff;

    FD_RECV_R(&nReadedCount, 4);

    BYTE_CHANGE_INT32 (nReadedCount);

    if ( nReadedCount >= nCountBuf )
    {
      return GIGI_E_INTERNAL_ERROR;
    };

    for (i = 0; i < nReadedCount; i++, pp++)
    {
      n = fd_recv_object (s, baseType, *pp, NULL );
      ERR_RETURN(n);
      nRecved += n;
    }

    return nRecved;
  }

/* Step 4 : for external type
 *
 */
  if ( baseType >= GIGI_TYPE_OBJECT_BEGIN)
  {
    GIGI_TYPE_DEF ty = gigi_type (baseType);
    return ty ? ty->_recv (s, type, buff, count) : GIGI_E_INTERNAL_ERROR;
  }

  return GIGI_E_INTERNAL_ERROR;
}

/* function fd_send_argv
 *          TYPE_ARGV and TYPE_STREAM supports
 */
static int fd_send_argv (GIGI_STREAM s, int type, const void * buff,
                         int nParamCount)
{
  char static_buf[256];
  char ** argv;
  int count, i, n, buff_size;
  int * argvLen;

  argv = *( (char ***)buff );
  
  count = nParamCount < 0 ? strings_count( (const char **)argv) 
                          : nParamCount;

  argvLen = STATIC_ALLOC (int *, static_buf, sizeof(int) * count );

  if (argvLen == NULL)
  {
    LOG_OUT_OF_MEMORY ();
    return GIGI_E_OUT_OF_MEMORY;
  };

  for (buff_size = sizeof (int), i = 0; i < count; i++)
  {
    argvLen[i] = (int)strlen (argv[i]) + 1;
    buff_size += argvLen[i];
  };

  n = fd_send_object (s, GIGI_TYPE_INT32, &buff_size, 0);
  CER(n);

  n = fd_send_object (s, GIGI_TYPE_INT32, &count, 0);
  CER(n);

  for (i = 0; i < count; i++)
  {
    n = gigi_fwrite (s, argv[i], argvLen[i]);
    CER(n);
  }

Cleanup:
  STATIC_FREE(argvLen,static_buf);

  return n >= 0 ? sizeof (int) + buff_size : n;
};


/* function fd_sendrecv_buffer
 *   alloc the buffer for channel, 
 *   the buffer will be alloc at two peer of channel
 */
static int fd_sendrecv_buffer (GIGI_STREAM s, int * nSize,
                               int sendNode, void ** buf)
{
  int n;

  *buf = NULL;

  if (sendNode)
  {
    if ( *nSize > 0 )
    {
      *buf = malloc ( *nSize );
    
      if ( *buf == NULL )
      {
        LOG_OUT_OF_MEMORY();
        *nSize = 0;
      }
    }

    n = fd_send_object (s, GIGI_TYPE_INT32, nSize, 0);
    CER(n);

    n = fd_recv_object (s, GIGI_TYPE_INT32, nSize, NULL);
    CER(n);
  }
  else
  {
    int nOK = *nSize > 0;

    n = fd_recv_object (s, GIGI_TYPE_INT32, nSize, NULL);
    CER (n);

    if ( !nOK )
    {
      *nSize = 0;
    }
    else if ( *nSize > 0 )
    {
      *buf = malloc ( *nSize );
      
      if ( *buf == NULL)
      {
        LOG_OUT_OF_MEMORY ();
        *nSize = 0;
      }
    }

    n = fd_send_object (s, GIGI_TYPE_INT32, nSize, 0);
    CER (n);
  }

Cleanup:;
  if ( n < 0 )
  {
    *nSize = 0;
  };

  if ( *nSize == 0 && *buf )
  {
    free (*buf);
    *buf = NULL;
  }

  return n;
};

/* function fd_recv_argv
 *
 */
static int fd_recv_argv (GIGI_STREAM s, int type, void * buff, int * count)
{
  int buff_size, nArgc, n;
  char * pBuf = NULL, **argv, *pBufData, *pBufEnd;

  n = fd_recv_object (s, GIGI_TYPE_INT32, &buff_size, NULL);
  ERR_RETURN (n);

  n = fd_recv_object (s, GIGI_TYPE_INT32, &nArgc, NULL);  
  ERR_RETURN (n);

  if ( nArgc < 0 || nArgc > 0xFFF || buff_size < 4 )
  {
    return GIGI_E_INTERNAL_ERROR;
  }

  pBuf     = (char *) malloc ( buff_size + (nArgc + 1) * sizeof (void *) );

  if (pBuf == NULL)
  {
    LOG_OUT_OF_MEMORY();
    return GIGI_E_OUT_OF_MEMORY;
  };

  argv     = (char **) pBuf;
  pBufData = pBuf + (nArgc + 1) * sizeof(void *);
  pBufEnd  = pBufData + buff_size - 4;

  if (buff_size > 4 )
  {
    n = gigi_fread (s, pBufData, buff_size - 4);
  }
  
  if ( n < 0 )
  {
    free (pBuf);
    return n;
  };
 
  pBufData[ buff_size - 4 ] = '\0';

  *((char ***)buff) = argv;

  if (count)
  {
    *count = nArgc;
  }

  for (; nArgc > 0; nArgc--, argv++ )
  {
    *argv = pBufData;
    
    while (pBufData  < pBufEnd && *pBufData != 0 )
    {
      pBufData++;
    }

    pBufData++;
  };

  *argv = NULL;

  return GIGI_OK;
};

/* function fd_send_stream
 *
 */
static int fd_send_stream (GIGI_STREAM fd, int type,
                           const void * buff, int count)
{
  GIGI_STREAM s    = (GIGI_STREAM)buff;
  char * pBuf = NULL;
  int n, buff_size   = s ? 64 * 1024 : 0;

  n = fd_sendrecv_buffer (fd, &buff_size, GIGI_TRUE, (void **)&pBuf);
  CER (n);

  if ( buff_size <= 0 )
  {
    return 0;
  };

  while ( s )
  {
    int readed = gigi_fread (s, pBuf, buff_size);

    if (readed <=0)
    {
      n = fd_send_object (fd, GIGI_TYPE_BUFFER, pBuf, 0 );
      CER (n);

      break;
    }

    n = fd_send_object (fd, GIGI_TYPE_BUFFER, pBuf, readed);
    CER (n);
  };

Cleanup:;

  if (pBuf)
  {
    free (pBuf);
  };

  return n;
}

/* function fd_recv_stream
 *
 */
static int fd_recv_stream (GIGI_STREAM fds, int type,
                           void * buff, int * count)
{
  GIGI_STREAM s = (GIGI_STREAM)buff;
  int n, buff_size, nTotalReaded = 0;
  char * pBuf = NULL;

  buff_size = s != NULL;
  n = fd_sendrecv_buffer (fds, &buff_size, GIGI_FALSE, (void **)&pBuf);
  CER (n);

  if ( buff_size == 0 )
  {
    return GIGI_OK;
  }

  while (s)
  {
    int nReaded = buff_size;

    n = fd_recv_object (fds, GIGI_TYPE_BUFFER, pBuf, &nReaded);
    CER (n);

    if (nReaded <= 0)
    {
      break;
    }

    nTotalReaded += nReaded;
    gigi_fwrite ( s, pBuf, nReaded);
  };

Cleanup:;
  if (count)
  {
    *count = nTotalReaded;
  };

  if (pBuf)
  {
    free (pBuf);
  }

  return n;
}

GIGI_ID aton_cache_key [257] = {0};
GIGI_ID aton_cache_id  [257] = {0};

/* function gigi_aton
 *
 */
GIGI_ID gigi_aton (GIGIONE context, const char* s, int def_port)
{
  int n;
  unsigned uIP;
  GIGI_ID hostAddr, hostID;
  GIGI_SOCKET fd;

  uIP = fd_getip(s, &def_port, NULL);
  hostAddr = GIGI_ID_MAKE(uIP, def_port, 0);
  hostID = 0;


  // get host ip,port
  //
  if ( uIP == 0 )
  {
    return 0;
  }

  n = ( ((unsigned)def_port << 16) | uIP ) % GIGI_COUNT_OF (  aton_cache_key );
  
  if ( aton_cache_key [n] == hostAddr )
  {
    return aton_cache_id [n];
  }

  if (context)
  {
    fd = gigi_socket (context, hostAddr, GIGI_SERVICE_GIGI, GIGI_FALSE, NULL);
  }
  else
  {
    fd = gigi_socket (NULL, hostAddr, GIGI_SERVICE_GIGI, GIGI_TRUE, NULL);
  }

  if (fd == NULL)
  {
    return 0;
  }

  n = GIGI_SERVICE_CMD_GETID;
  
  n = gigi_send_object (fd, GIGI_TYPE_INT32, &n, 0);
  
  if ( n>=0 )
  {
    n = gigi_recv_object (fd, GIGI_TYPE_ID, &hostID, NULL);
  }

  gigi_closesocket (fd, 0);

  if ( n>=0 )
  {
    aton_cache_key [n] = hostAddr;
    aton_cache_id  [n] = hostID;

    return hostID;
  }
  else
  {
    return 0;
  }
}

/* function gigi_aton_list
 *
 */
int gigi_aton_list (GIGIONE context, const char* addrs,
                    int def_port, GIGI_ID * id, int count)
{
  char ** list;
  int i, len;

  list = strings_split (addrs,',');
  len = strings_count (list);

  if (len >0 && len <= count)
  {
    count = len;

    for ( i = 0; i < count; i++,id++)
    {
      *id = gigi_aton (context, list[i], def_port);

      if ( *id == 0 )
      {
        count = -1;
        break;
      }
    }
  }
  else
  {
    count = -1;
  }

  if (list)
  {
    free (list);
  }

  return count;
};

/* function gigi_ntoa
 *
 */
char * gigi_ntoa (GIGI_ID node, char * addr)
{
  unsigned uIP;
  unsigned short uPORT;
  unsigned char * pA;
  struct hostent * he;

  uIP = htonl ( GIGI_ID_IP(node) );
  uPORT =  GIGI_ID_PORT (node);
  he = gethostbyaddr ( (const char*)&uIP, 4, AF_INET);
  
  if (he)
  {
    strcpy (addr, he->h_name);

    if (uPORT > 0 )
    {
      sprintf (addr + strlen(addr), ":%u", uPORT );
    }
  }
  else
  {
    pA = (unsigned char*)&uIP;
    sprintf (addr,"%u.%u.%u.%u:%u", pA[0], pA[1], pA[2], pA[3], uPORT);
  }

  return addr;
}


/* function gigi_send_object
 *
 */
int gigi_send_object (GIGI_SOCKET fd, int type, const void* buff, int count)
{
  return fd_send_object ( &fd->_tcp_fd._vt, type, buff, count);
}

/* function gigi_recv_object
 *
 */
int gigi_recv_object (GIGI_SOCKET fd, int type, void* buff, int * count)
{
  return fd_recv_object ( &fd->_tcp_fd._vt, type, buff, count);
}

/* function gigi_recv
 *
 */
int gigi_recv (GIGI_SOCKET fd, void * buff, int size)
{
  int n = GIGI_E_READ_ERROR;
  
  if (fd && fd->_error == 0)
  {
    n = gigi_fread (&fd->_tcp_fd._vt, buff, size);

    if ( n < 0 )
    {
      fd->_error = 2;
    }
  }

  return n;
}

/* function gigi_send
 *
 */
int gigi_send (GIGI_SOCKET fd, const void* buff, int size)
{
  int n = GIGI_E_WRITE_ERROR;

  if (fd && fd->_error == 0)
  {
    n = gigi_fwrite ( &fd->_tcp_fd._vt, buff, size);

    if ( n < 0 )
    {
      fd->_error = 1;
    }
  };

  return n;
}

/* function gigi_set_callback
 *
 */
void * gigi_set_callback (GIGIONE context, GIGI_CALLBACK func, void * func_ctx)
{
  gigi_callback_info * cbroot;
  gigi_callback_info * cb;

  GIGI_HANDLE_CHECK (NULL);

  MUTEX_LOCK (context->_lock);

  cbroot = (gigi_callback_info * ) & (context->_callback_list);

  cb = NEW_OBJECT (gigi_callback_info);

  if (cb)
  {
    cb->_ctx  = func_ctx;
    cb->_func = func;
    cb->_context   = context;

    RING_PUSH_BACK ( cbroot, cb);
  }
  else
  {
    LOG_OUT_OF_MEMORY();
  }

  MUTEX_UNLOCK (context->_lock);

  return cb;
};

/* function gigi_kill_callback
 *
 */
int gigi_kill_callback (void * id)
{
  gigi_callback_info * cb;

  cb = (gigi_callback_info * )id;

  if (cb == NULL || 
      cb->_context == NULL || 
      RING_IS_EMPTY (cb) )
  {
    return GIGI_E_INVALID_HANDLE;
  }

  MUTEX_LOCK (cb->_context->_lock);

  RING_DEL (cb);

  MUTEX_UNLOCK (cb->_context->_lock);

  free(cb);

  return GIGI_OK;
}

/* function gigi_get_peer_id
 *
 */
GIGI_ID gigi_get_peer_id (GIGI_SOCKET fd)
{
  GIGI_ID id;
  
  id = fd ? fd->_node_pool->_node_id : 0;

  return id;
}

/* function gigi_gen_id
 * 
 */
GIGI_ID gigi_gen_id (GIGIONE context)
{
  GIGI_ID sID;

  MUTEX_LOCK ( context->_lock );

  sID = GIGI_ID_MAKE ( 
      GIGI_ID_IP( context->_my_node_id ),
      GIGI_ID_PORT( context->_my_node_id ),
      context->_last_service_id++ );

  if (context->_last_service_id == 0)
  {
    context->_last_service_id ++;
  }

  MUTEX_UNLOCK( context->_lock );

  return sID;
};

/* function : gigi_register_mpi_func
 *
 */
int gigi_register_mpi_func (GIGIONE context, GMPI_FUNC_DEF funcs, int reg)
{
  gmpi_func_list * root, * i, * iEND;

  GIGI_HANDLE_CHECK (GIGI_E_INTERNAL_ERROR);

  root = (gmpi_func_list*)&(context->_func_list_root);

  if (reg)
  {
    gmpi_func_list* nd   = NEW_OBJECT (gmpi_func_list);
    
    if (nd == NULL) 
    {
      return GIGI_E_OUT_OF_MEMORY;
    };

    nd->_funcs = funcs;
    RING_PUSH_BACK (root, nd);
  }
  else
  {
    i    = RING_BEGIN (root, gmpi_func_list*);
    iEND = RING_END   (root, gmpi_func_list*);
    
    for (;i != iEND; RING_NEXT (i) )
    {
      if ( i->_funcs == funcs )
      {
        RING_DEL (i);
        free (i);
        break;
      }
    }

    memset (context->_func_cache, 0, sizeof (context->_func_cache));
  }

  return GIGI_OK;
}

/* function gigi_mpi_func_def
 *
 */
GMPI_FUNC_DEF gigi_mpi_func_def (GIGIONE context, const char * func_name)
{
  gmpi_func_list * i, * iEND;
  unsigned int hash_ind;
  GMPI_FUNC_DEF pCache;

  GIGI_HANDLE_CHECK (NULL);

  hash_ind = string_hash (0, func_name, (int)strlen (func_name));

  hash_ind = ((unsigned)hash_ind) % GIGI_COUNT_OF (context->_func_cache);
  pCache = context->_func_cache [hash_ind];

  if ( pCache && (pCache->_name == func_name || strcmp( pCache->_name, func_name) == 0) )
  {
#ifdef GIGI_DEBUG
    gmpi_func_cache_hit ++;
#endif
    return pCache;
  }

#ifdef GIGI_DEBUG
  gmpi_func_cache_missed ++;
#endif

  i    = RING_BEGIN (&(context->_func_list_root), gmpi_func_list * );
  iEND = RING_END   (&(context->_func_list_root), gmpi_func_list * );
  
  for (;i != iEND; RING_NEXT (i) )
  {
    GMPI_FUNC_DEF f = i->_funcs;

    for (; f->_name; f++ )
    {
      if ( strcmp (f->_name, func_name) == 0 )
      {
        context->_func_cache [hash_ind] = f;
        return f;
      }
    }
  }

  return NULL;
}

